Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultibodyPlant hinders writing user non-finite values to context #22594

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

SeanCurtis-TRI
Copy link
Contributor

@SeanCurtis-TRI SeanCurtis-TRI commented Feb 6, 2025

MultibodyPlant::SetFoo(context, vector): when writing user-provided configuration values (qs and vs) into the context, we throw if the input has NaNs.

Includes the following APIs:

  • SetPositions() - 3 overloads
  • SetPositionsAndVelocities() - 2 overloads
  • SetDefaultPositions() - 2 overloads
  • SetVelocities() - 3 overloads
  • SetVelocitiesInArray()

This change is Reviewable

@SeanCurtis-TRI SeanCurtis-TRI added the release notes: fix This pull request contains fixes (no new features) label Feb 6, 2025
Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+(release notes: fix)
+@amcastro-tri for feature review, please.

Reviewable status: LGTM missing from assignee amcastro-tri, needs platform reviewer assigned, needs at least two assigned reviewers

@SeanCurtis-TRI SeanCurtis-TRI changed the title MultibodyPlant hinders writing user NaN to context MultibodyPlant hinders writing user non-finite values to context Feb 7, 2025
Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amcastro-tri just wanted to ping you on this.

Reviewable status: LGTM missing from assignee amcastro-tri, needs platform reviewer assigned, needs at least two assigned reviewers

Copy link
Collaborator

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 2 unresolved discussions, LGTM missing from assignee amcastro-tri, needs platform reviewer assigned, needs at least two assigned reviewers


multibody/plant/multibody_plant.h line 2675 at r1 (raw file):

    this->ValidateContext(context);
    DRAKE_THROW_UNLESS(q_v.size() == (num_positions() + num_velocities()));
    if constexpr (!std::is_same_v<T, symbolic::Expression>) {

There is no reason to if-constexpr here. The DRAKE_THROW_UNLESS already turns itself into a no-op when asked to check a symbolic assertion.

Ditto throughout.

--- a/multibody/plant/multibody_plant.h
+++ b/multibody/plant/multibody_plant.h
@@ -2672,12 +2672,10 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
       const Eigen::Ref<const VectorX<T>>& q_v) const {
     this->ValidateContext(context);
     DRAKE_THROW_UNLESS(q_v.size() == (num_positions() + num_velocities()));
-    if constexpr (!std::is_same_v<T, symbolic::Expression>) {
+    DRAKE_THROW_UNLESS(all_of(q_v, [](const T& t) {
       using std::isfinite;
-      DRAKE_THROW_UNLESS(all_of(q_v, [](const T& t) {
-        return isfinite(t);
-      }));
-    }
+      return isfinite(t);
+    }));
     internal_tree().GetMutablePositionsAndVelocities(context) = q_v;
   }
 

multibody/plant/multibody_plant.cc line 1720 at r1 (raw file):

  DRAKE_MBP_THROW_IF_NOT_FINALIZED();
  DRAKE_THROW_UNLESS(q.size() == num_positions());
  if constexpr (!std::is_same_v<T, symbolic::Expression>) {

The q here is already double-valued. There is no reason to opt-out for Expression.

Ditto for elsewhere when the q is not T-typed.

MultibodyPlant::SetFoo(context, vector): when writing user-provided
configuration values (qs and vs) into the context, we throw if the input
has non-finite values.

Includes the following APIs:
  - SetPositions() - 3 overloads
  - SetPositionsAndVelocities() - 2 overloads
  - SetDefaultPositions() - 2 overloads
  - SetVelocities() - 3 overloads
  - SetVelocitiesInArray()
Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jwnimmer-tri given I've only got a couple of days left this week, could I prevail upon you to feature review this?

Reviewable status: LGTM missing from assignee amcastro-tri, needs platform reviewer assigned, needs at least two assigned reviewers


multibody/plant/multibody_plant.h line 2675 at r1 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

There is no reason to if-constexpr here. The DRAKE_THROW_UNLESS already turns itself into a no-op when asked to check a symbolic assertion.

Ditto throughout.

--- a/multibody/plant/multibody_plant.h
+++ b/multibody/plant/multibody_plant.h
@@ -2672,12 +2672,10 @@ class MultibodyPlant : public internal::MultibodyTreeSystem<T> {
       const Eigen::Ref<const VectorX<T>>& q_v) const {
     this->ValidateContext(context);
     DRAKE_THROW_UNLESS(q_v.size() == (num_positions() + num_velocities()));
-    if constexpr (!std::is_same_v<T, symbolic::Expression>) {
+    DRAKE_THROW_UNLESS(all_of(q_v, [](const T& t) {
       using std::isfinite;
-      DRAKE_THROW_UNLESS(all_of(q_v, [](const T& t) {
-        return isfinite(t);
-      }));
-    }
+      return isfinite(t);
+    }));
     internal_tree().GetMutablePositionsAndVelocities(context) = q_v;
   }
 

Done

@jwnimmer-tri
Copy link
Collaborator

How about @sherm1? I think feature review should be someone qualified to assess the performance regression hazard.

Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As he's platform review tomorrow, would you be willing to cover platform?

Reviewable status: LGTM missing from assignee amcastro-tri, needs platform reviewer assigned, needs at least two assigned reviewers

Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-@amcastro-tri +@sherm1 for feature review.

Reviewable status: LGTM missing from assignee sherm1(platform), needs at least two assigned reviewers

@jwnimmer-tri
Copy link
Collaborator

Sure.

Copy link
Contributor Author

@SeanCurtis-TRI SeanCurtis-TRI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

Reviewable status: LGTM missing from assignee sherm1(platform), needs at least two assigned reviewers

Copy link
Member

@sherm1 sherm1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature :lgtm: with a request for a nicer error message.

Regarding performance: There is a significant bump to the cost of these methods (maybe 30% ish(?) since there is additional copying and cache invalidation being done) but I think it is worth it for these user-facing APIs which I would not expect to be used in inner loops. I checked the integrators -- they work directly with the (unchecked) Context API, which is good since they set state frequently, and any internal code can dodge the top level API if needed.

Reviewed 1 of 3 files at r1, 2 of 2 files at r2, all commit messages.
Reviewable status: 1 unresolved discussion, needs at least two assigned reviewers


multibody/plant/multibody_plant.h line 2675 at r2 (raw file):

    this->ValidateContext(context);
    DRAKE_THROW_UNLESS(q_v.size() == (num_positions() + num_velocities()));
    DRAKE_THROW_UNLESS(all_of(q_v, [](const T& t) {

minor: I'm concerned about the inscrutable error message this will generate for a relatively common user-facing error condition (esp. for PyDrake users). Since this is done repeatedly (possibly rule-of-3 dodge) anyway, why not make a little ThrowIfNonFinite(func, ...) helper that generates a good message and use that everywhere?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release notes: fix This pull request contains fixes (no new features)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants